package org.persistente.banco;

import java.sql.*;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.persistente.PersistenteException;
import org.persistente.esquema.CampoAbstrato;
import org.persistente.esquema.CampoInteger;
import org.persistente.esquema.Tabela;

/**
 *
 * @author philipe
 */
public class ConexaoSqlite extends ConexaoBancoDados {

    public ConexaoSqlite(String endereco) {
        this(endereco, "", "");
    }

    public ConexaoSqlite(String endereco, String usuario, String senha) {
        super(endereco, usuario, senha);
    }

    @Override
    public String getClasse() {
        return "org.sqlite.JDBC";
    }

    @Override
    public String getStringConexao() {
        return "jdbc:sqlite:" + endereco;
    }
    
    @Override
    public String createSql(Tabela tabela) {
        return createSql(tabela, tabela.getNome());
    }

    public String createSql(Tabela tabela, String nome) {
        StringBuilder sb = new StringBuilder();
        sb.append("create table if not exists ");
        sb.append(nome);
        sb.append("(");
        Set<CampoAbstrato> chaves = tabela.getChaves();
        Set<CampoAbstrato> campos = tabela.getCampos();

        if (chaves.size() == 1 && chaves.iterator().next() instanceof CampoInteger) {
            Iterator<CampoAbstrato> it = chaves.iterator();
            CampoAbstrato campo = chaves.iterator().next();
            CampoAbstrato chave = campo;
            sb.append(campo.getNome());
            sb.append(" integer primary key autoincrement");
            it = campos.iterator();
            while (it.hasNext()) {
                campo = it.next();
                if(campo != chave){
                    sb.append(',');
                    sb.append(campo.getNome());
                    sb.append(" ");
                    sb.append(campo.tipoSql());
                }
            }
        } else {
            CampoAbstrato campo;
            Iterator<CampoAbstrato> it = campos.iterator();
            if (it.hasNext()) {
                campo = it.next();
                sb.append(campo.getNome());
                sb.append(" ");
                sb.append(campo.tipoSql());
            }
            while (it.hasNext()) {
                campo = it.next();
                sb.append(',');
                sb.append(campo.getNome());
                sb.append(" ");
                sb.append(campo.tipoSql());
            }
            if(chaves.size() > 0){
                sb.append(", primary key(");
                it = chaves.iterator();
                if(it.hasNext()){
                    campo = it.next();
                    sb.append(campo.getNome());
                }
                while(it.hasNext()){
                    campo = it.next();
                    sb.append(",");
                    sb.append(campo.getNome());
                }
                sb.append(")");
            }
        }

        sb.append(")");
        return sb.toString();
    }

    private int seq = 0;
    
    
    
    @Override
    public synchronized void atualizaEsquema(Tabela tabela, Connection con){
        String sql = createSql(tabela);
        if(sql.isEmpty()){
            return;
        }
        String savepoint = "seq" + seq++;
        Statement st = null;
        try {
            st = con.createStatement();
            
            
            PreparedStatement ps = con.prepareStatement("select sql from sqlite_master where name=?");
            ps.setString(1, tabela.getNome());
            ResultSet rs = ps.executeQuery();
            String sql_anterior = null;
            if(rs.next()){
                sql_anterior = rs.getString("sql");
            }
            rs.close();
            ps.close();
            
            if(sql_anterior == null){
                st.executeUpdate(sql);
            } else if(tabelaMudou(sql, sql_anterior)){
                rs = st.executeQuery("select count(*) from " + tabela.getNome());
                int count = rs.getInt(1);
                rs.close();
                if(count == 0){
                    st.executeUpdate("drop table " + tabela.getNome());
                    st.executeUpdate(sql);
                } else{
                    
                    
                    System.out.println("Alterando esquema da tabela " + tabela.getNome() + "...");
                    
                    HashSet<String> anteriores = new HashSet<String>();
                    HashSet<String> atuais = new HashSet<String>();
                    rs = st.executeQuery("select * from " + tabela.getNome() + " limit 1");
                    ResultSetMetaData md = rs.getMetaData();
                    count = md.getColumnCount();
                    for(int i = 1; i <= count; ++i){
                        anteriores.add(md.getColumnName(i));
                    }
                    for(CampoAbstrato campo : tabela.getCampos()){
                        atuais.add(campo.getNome());
                    }
                    
                    
                    HashSet<String> comuns = new HashSet<String>();
                    for(String campo : anteriores){
                        if(atuais.contains(campo)){
                            comuns.add(campo);
                        }
                    }
                    
                    StringBuilder colunas = new StringBuilder();
                    for(String c : comuns){
                        colunas.append(c).append(',');                        
                    }
                    String cols = colunas.substring(0, colunas.length()-1);
                    
                    
                    String temp = "__" + tabela.getNome() + "_temp_";
                    
                    st.executeUpdate("SAVEPOINT " + savepoint + ";");
                    
                    
                    
                    sql = createSql(tabela, temp);
                    st.executeUpdate(sql);
                    
                    sql = "insert into " + temp + "(" + cols + ") select " + cols + " from " + tabela.getNome();
                    st.executeUpdate(sql);
                    
                    sql = "drop table " + tabela.getNome();
                    st.executeUpdate(sql);
                    
                    sql = "alter table " + temp + " rename to " + tabela.getNome();
                    st.executeUpdate(sql);
                    
                    
                    st.executeUpdate("RELEASE " + savepoint + ";");                    
                }
            }           
            
            
        } catch (SQLException ex) {
            ex.printStackTrace();
            try {
                st.execute("ROLLBACK TO SAVEPOINT " + savepoint);
            } catch (SQLException ex1) {
                ex1.printStackTrace();
            }
            throw new PersistenteException(ex);
        } finally{
            Conexoes.fecha(st);
        }
    }

    private boolean tabelaMudou(String sql, String sql_anterior) {        
        if(sql_anterior == null){
            return false;
        }
        
        int i1 = sql.indexOf('(');
        int i2 = sql_anterior.indexOf('(');
        
        if(i1 == -1 || i2 == -1){
            return true;
        }       
        
        return !sql.substring(i1).equals(sql_anterior.substring(i2));
    }


}
